﻿// -----------------------------------------------------------------------
// <copyright file="ConnectionStringBuilder.cs" company="JSC Centido">
//  Copyright (c) JSC Centido. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

namespace Centido.Core
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Text.RegularExpressions;

    /// <summary>
    ///   A generic connection string builder.
    /// </summary>
    public abstract class ConnectionStringBuilder
    {
        /// <summary>
        ///   The driver name.
        /// </summary>
        private string _driver = string.Empty;

        /// <summary>
        ///   The database name
        /// </summary>
        private string _dbName = string.Empty;

        /// <summary>
        ///   The server port number
        /// </summary>
        private int _portNumber = default(int);

        /// <summary>
        ///   The server location
        /// </summary>
        private string _serverLocation = Environment.MachineName;

        /// <summary>
        ///   The user name
        /// </summary>
        private string _userName = string.Empty;

        /// <summary>
        ///   The user password
        /// </summary>
        private string _userPassword = string.Empty;


        /// <summary>
        ///   Gets the ODBC driver invariant name.
        /// </summary>
        public abstract string DbProviderName { get; }


        /// <summary>
        ///   Gets or sets the driver name.
        /// </summary>
        public string Driver
        {
            get { return _driver; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _driver = value;
            }
        }


        /// <summary>
        ///   Gets or sets the server location
        /// </summary>
        public string Host
        {
            get { return _serverLocation; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _serverLocation = value;
            }
        }


        /// <summary>
        ///   Gets or sets the server port number.
        /// </summary>
        public int PortNumber
        {
            get { return _portNumber; }
            set { _portNumber = value; }
        }


        /// <summary>
        ///   Gets or sets the database name.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")]
        public string DbName
        {
            get { return _dbName; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _dbName = value;
            }
        }


        /// <summary>
        ///   Gets or sets the user name.
        /// </summary>
        public string UserName
        {
            get { return _userName; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _userName = value;
            }
        }


        /// <summary>
        ///   Gets or sets the user password.
        /// </summary>
        public string UserPassword
        {
            get { return _userPassword; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                _userPassword = value;
            }
        }


        /// <summary>
        ///   Gets the target rdbms product name.
        /// </summary>
        public abstract string ProductName
        {
            get;
        }


        /// <summary>
        ///   True if the user has elevated privileges; otherwise, false.
        /// </summary>
        public bool HasElevatedPrivileges { get; set; }


        /// <summary>
        ///   Builds the connection string.
        /// </summary>
        /// <returns></returns>
        public abstract string Build();


        /// <summary>
        ///   Parses the specified connection string.
        /// </summary>
        /// <param name="connectionString">The connection string.</param>
        public abstract void Parse(string connectionString);


        /// <summary>
        ///   Parses the driver name from the specified connection string
        /// </summary>
        /// <param name="connectionString">The connection string.</param>
        /// <returns>Returns </returns>
        protected static string ParseDriverName(string connectionString)
        {
            if (connectionString == null)
                throw new ArgumentNullException("connectionString");

            // Searching for parameter name
            var parameterClauseMatch = Regex.Match(
                connectionString,
                @"Driver\s*=\s*{[\w|.|\-|,|\\|/|:| ]+}\s*;?",
                RegexOptions.IgnoreCase);

            if (parameterClauseMatch.Success)
            {
                var userNameClauseParts = parameterClauseMatch.Value.Split('=');

                return userNameClauseParts[1].Trim(';', ' ', '\t')
                    .Trim('}', '{');
            }

            return null;
        }


        /// <summary>
        ///   Parses a string parameter value from the connection string
        /// </summary>
        /// <param name = "connectionString">The connection string.</param>
        /// <param name = "parameterName">The parameter name.</param>
        /// <returns>Returns the parameter value.</returns>
        protected static string ParseStringParameter(string connectionString, string parameterName)
        {
            if (connectionString == null)
                throw new ArgumentNullException("connectionString");
            if (parameterName == null)
                throw new ArgumentNullException("parameterName");
            if (parameterName.Trim().Length == 0)
                throw new ArgumentException("Parameter name cannot be empty.");

            // Searching for parameter name
            var parameterClauseMatch = Regex.Match(
                connectionString,
                string.Format(@"{0}\s*=\s*[\w|.|\-|,|\\|/|:| ]+\s*;?", parameterName.Replace(" ", @"\s+")),
                RegexOptions.IgnoreCase);

            if (parameterClauseMatch.Success)
            {
                var userNameClauseParts = parameterClauseMatch.Value.Split('=');

                return userNameClauseParts[1].Trim(';', ' ', '\t');
            }

            return null;
        }

        /// <summary>
        ///   Parses an integer parameter value from the connection string.
        /// </summary>
        /// <param name = "connectionString">The connection string.</param>
        /// <param name = "parameterName">The parameter name.</param>
        /// <returns>Returns the parameter value.</returns>
        protected static int? ParseIntegerParameter(string connectionString, string parameterName)
        {
            if (connectionString == null)
                throw new ArgumentNullException("connectionString");
            if (parameterName == null)
                throw new ArgumentNullException("parameterName");

            // Searching for parameter name
            var parameterClauseMatch = Regex.Match(
                connectionString,
                string.Format(@"{0}\s*=\s*\d+\s*;?", parameterName.Replace(" ", @"\s+")),
                RegexOptions.IgnoreCase);

            if (parameterClauseMatch.Success)
            {
                var userNameClauseParts = parameterClauseMatch.Value.Split('=');

                return int.Parse(userNameClauseParts[1].Trim(';', ' ', '\t'));
            }

            return null;
        }

        /// <summary>
        ///   Parses a bool parameter value from the connection string.
        /// </summary>
        /// <param name = "connectionString">The connection string.</param>
        /// <param name = "parameterName">The parameter name.</param>
        /// <returns>Returns the parameter value.</returns>
        protected static bool? ParseBoolParameter(string connectionString, string parameterName)
        {
            if (connectionString == null)
                throw new ArgumentNullException("connectionString");
            if (parameterName == null)
                throw new ArgumentNullException("parameterName");

            // Searching for parameter name
            var parameterClauseMatch = Regex.Match(
                connectionString,
                string.Format(@"{0}\s*=\s*(true|false|yes|no)\s*;?", parameterName.Replace(" ", @"\s+")),
                RegexOptions.IgnoreCase);

            if (parameterClauseMatch.Success)
            {
                var userNameClauseParts = parameterClauseMatch.Value.Split('=');

                var boolValueStr = userNameClauseParts[1].Trim(';', ' ', '\t');

                return boolValueStr == "true" || boolValueStr == "yes";
            }

            return null;
        }


        /// <summary>
        ///   Creates a new connection string builder of the current type.
        /// </summary>
        /// <returns>Returns a new connection string builder that has the same type
        /// as the current one.</returns>
        public abstract ConnectionStringBuilder CreateNew();


        /// <summary>
        ///   Retrieves the corresponding LoBDb.NET metadata installer.
        /// </summary>
        /// <returns>Returns the corresponding LoBDb.NET metadata installer
        /// if there is one; otherwise, returns null.</returns>
        public Installer GetInstaller()
        {
            return Installers.GetInstaller(ProductName);
        }
    }
}